#include "service.h"
#include "common.h"
#include <string>
#include <muduo/base/Logging.h>
using namespace muduo;
using namespace std;

Service* Service::instance() {
    static Service service;
    return &service;
}

Service::Service() {
    msgHandlerMap.insert({E_MESSAGE_TYPE::eLogin, std::bind(&Service::login, this, _1, _2, _3)});
    msgHandlerMap.insert({E_MESSAGE_TYPE::eRegister, std::bind(&Service::reg, this, _1, _2, _3)});
    msgHandlerMap.insert({E_MESSAGE_TYPE::eChatMsg, std::bind(&Service::chat, this, _1, _2, _3)});
    

    // 连接redis服务器
    if (_redis.connect())
    {
        // 设置上报消息的回调
        _redis.init_notify_handler(std::bind(&Service::handleRedisSubscribeMessage, this, _1, _2));
    }
}

void Service::login(const TcpConnectionPtr &conn, json &js, Timestamp time) {
    string strAccount = js["Account"];
    string strPassword = js["Password"];
    User user = userModel.query(strAccount);
    LOG_INFO << strPassword;
    LOG_INFO << strAccount;
    LOG_INFO << user.getPassword();
    if (user.getPassword() == strPassword) {

        if (user.getState() == strOnLine)
        {
            //如果当前用户已经登录，不允许重复登录
            return;
        }

        //登录成功
        {
            lock_guard<mutex> lock(connectMutex);
            userConnectMap.insert({strAccount, conn});
        }
        
        //更新该用户的状态
        user.setState(strOnLine);
        userModel.updateState(user);

        // id用户登录成功后，向redis订阅channel(id)
        _redis.subscribe(strAccount); 

        //回复
        json response;
        response["Qo"] = "QoQoQo";
        response["MessageType"] = eLoginAck;
        response["NickName"] = user.getNickName();
        response["state"] = eTrue;
        response["userstate"] = strOnLine;

        //返回该用户的好友信息
        vector<User> userVec = friendModel.searchFriends(strAccount);
        if (!userVec.empty())
        {
            vector<string> vec2;
            for (User &user : userVec)
            {
                json js;
                js["friendAccount"] = user.getAccount();
                js["friendNickName"] = user.getNickName();
                js["state"] = user.getState();
                vec2.push_back(js.dump());
            }
            response["friends"] = vec2;
        }
        conn->send(response.dump());
    } else {
        json response;
        response["Qo"] = "QoQoQo";
        response["MessageType"] = eLoginAck;
        response["state"] = eFalse;
        conn->send(response.dump());
    }
    LOG_INFO << "do login service!!!";
}

//注册
void Service::reg(const TcpConnectionPtr &conn, json &js, Timestamp time) {
    string strNicknName = js["NickName"];
    string strPassword = js["Password"];
    User user;
    user.setNickName(strNicknName);
    user.setPassword(strPassword);
    bool state = userModel.insert(user);
    if (state) {
        //注册成功
        json response;
        response["Qo"] = "QoQoQo";
        response["MessageType"] = eRegisterAck;
        response["state"] = eTrue;
        response["Account"] = user.getAccount();
        conn->send(response.dump());

        //给新用户添加10000好友
        friendModel.addFriend(user.getAccount(), "10000");
    } else {
        //注册失败
        json response;
        response["Qo"] = "QoQoQo";
        response["MessageType"] = eRegisterAck;
        response["state"] = eFalse;
        conn->send(response.dump());
    }
}

//聊天
void Service::chat(const TcpConnectionPtr &conn, json &js, Timestamp time) {
    LOG_INFO << "chat";
    string strToAccount = js["To"];

    {
        lock_guard<mutex> lock(connectMutex);
        LOG_INFO << "strToAccount:" << strToAccount;
        auto it = userConnectMap.find(strToAccount);
        for (auto m : userConnectMap) {
            LOG_INFO << m.first;
        }
        if (it != userConnectMap.end())
        {
            // toid在线，转发消息   服务器主动推送消息给toid用户
            LOG_INFO << "send" << js.dump();
            it->second->send(js.dump());
            return;
        }
    }

    // 查询toid是否在线 
    User user = userModel.query(strToAccount);
    if (user.getState() == "online")
    {
        _redis.publish(strToAccount, js.dump());
        return;
    }

    // toid不在线，存储离线消息
    //_offlineMsgModel.insert(toid, js.dump());
}

MsgHandler Service::getHandler(int msgid) {
    LOG_INFO << "msgid: " << msgid;
    auto it = msgHandlerMap.find(msgid);
    if (it == msgHandlerMap.end()) {
        return [=](const TcpConnectionPtr &conn, json &js, Timestamp time){
            LOG_ERROR << "msgid: " << msgid << "can not find handler!";
        };
    } else {
        return msgHandlerMap[msgid];
    }
}

void Service::clientCloseException(const TcpConnectionPtr &conn) {
    User user;
    {
        lock_guard<mutex> lock(connectMutex);
        for (auto it = userConnectMap.begin(); it != userConnectMap.end(); ++it)
        {
            if (it->second == conn)
            {
                // 从map表删除用户的链接信息
                LOG_INFO << "client close exception: " << it->first; 
                user.setAccount(it->first);
                userConnectMap.erase(it);
                break;
            }
        }
    }
    //更新用户状态信息
    user.setState("offline");
    userModel.updateState(user);

    // 用户注销，相当于就是下线，在redis中取消订阅通道
    _redis.unsubscribe(user.getAccount()); 
}

// 从redis消息队列中获取订阅的消息
void Service::handleRedisSubscribeMessage(string strAccount, string msg)
{
    lock_guard<mutex> lock(connectMutex);
    auto it = userConnectMap.find(strAccount);
    if (it != userConnectMap.end())
    {
        it->second->send(msg);
        return;
    }

    // 存储该用户的离线消息
   // _offlineMsgModel.insert(userid, msg);
}